package bottle.jack;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;

import org.json.JSONArray;
import org.json.JSONObject;

import bottle.jack.User.UserData;

import com.google.code.morphia.Datastore;
import com.google.code.morphia.Morphia;
import com.google.code.morphia.query.Query;
import com.mongodb.Mongo;


public class MongoDAO {

  private static MongoDAO instance;
  public Mongo mongo;
  public Morphia morphia;
  public static Datastore userStore;
  public static Datastore messageStore;
  public static Datastore locationTree;
  private boolean exists = true;
  protected MongoDAO() {	   
    try {
      mongo = new Mongo();
      morphia = new Morphia();
      morphia.map(User.class).map(User.UserData.class);
      ///	morphia.map(Message.class).map(Message.MessageData.class);
      userStore = morphia.createDatastore(mongo, "userDB");
      messageStore = morphia.createDatastore(mongo, "messageDB");
      //  locationTree = morphia.createDatastore(mongo, "locationTree");

    } catch (UnknownHostException e) {
      System.out.println("Cannot create database");
    }
    User.UserData q;
    try{
      q = userStore.createQuery(User.UserData.class).get();
    }catch(Exception e)
    {
      System.out.println("There are no users in the database.");
    }

  }
  public static MongoDAO getInstance() {		   
    if(instance == null) {
      instance = new MongoDAO();
    }
    return instance;
  }
  public void AddUser(User u)
  {
    userStore.save(u.data);
  }
  /*public void AddMessage(Message s)
	   {
		   messageStore.save(s.data);
	   }
	   public List<Message> GetAllMessages()
	   {
		   List<Message.MessageData> messageD = messageStore.createQuery(Message.MessageData.class).asList();
			List<Message> messages = new ArrayList<Message>(0);
			for (Message.MessageData u : messageD)
			{
				Message k = new Message(u.rootSender, u.destination, u.messageData, u.iterationsLeft);

			}
			return messages;

	   }*/
  public List<User> GetAllUsers()
  {
    List<User.UserData> usersD = userStore.createQuery(User.UserData.class).asList();
    List<User> users = new ArrayList<User>(0);
    for (User.UserData u : usersD)
    {
      User k = new User(u.identity, u.nickname, u.location);
      k.messages = u.messages;
      k.region = u.region;
      users.add(k);
    }
    return users;
  }
  public User GetNearestUser(Location location, User user)
  {
    return LocationProcessing.getInstance().FindNearestUser(GetAllUsers(), location, user);
  }

  public User UserDataToUser(User.UserData data)
  {
    User s = new User(data.identity, data.nickname, data.location);
    s.messages = data.messages;
    s.region = data.region;
    return s;
  }

  private List<User> GetUsersInRegion(int region, int number) {
    // TODO Auto-generated method stub
    List<User.UserData> usersD = userStore.createQuery(User.UserData.class).field("region").equal(region).asList();
    if(usersD.size() < 2) {
      usersD = userStore.createQuery(User.UserData.class).field("region").equal(++region).asList();
    }
    List<User> users = new ArrayList<User>(0);
    for (User.UserData u : usersD)
    {

      users.add(UserDataToUser(u));
    }
    return users;
  }
  public User GetUserById(String string) {
    User.UserData usD = null;
    User user = null;
    try{
      usD = userStore.get(User.UserData.class, string);
      user = UserDataToUser(usD);
    }catch(NullPointerException e)
    {
      System.out.println("There is no user in the database with that ID");
    }
    return user;
  }
  public User GetUserByNickName(String string) {
    Query<User.UserData> query = userStore.createQuery(User.UserData.class).field("nickname").equal(string);
    User.UserData usD = null;
    try{
      usD = query.get();
    }catch(NullPointerException e)
    {
      System.out.println("There is no user in the database with that nickname");
    }
    /*User k = new User(usD.identity, usD.nickname, usD.location);
		k.messages = usD.messages;
		k.region = usD.region;*/
    return UserDataToUser(usD);
  }
  public void UpdateUser(User user) {
    Query<User.UserData> updateQuery = userStore.createQuery(User.UserData.class).field("identity").equal(user.identity);
    /*UpdateOperations<User.UserData> ops;
		//getDatastore().getKey(user);
		ops = userStore.createUpdateOperations(User.UserData.class).set("messages", userStore.get(User.UserData.class, user.identity));
		userStore.update(updateQuery, ops);
		ops = userStore.createUpdateOperations(User.UserData.class).set("location", user.location);
		userStore.update(updateQuery, ops);
		ops = userStore.createUpdateOperations(User.UserData.class).set("region", user.region);
		userStore.update(updateQuery, ops);*/
    userStore.delete(updateQuery);
    userStore.save(user.data);
  }

  /**
   * Checks if a device exists within the database.
   * @param deviceId the device's id.
   * @return whether the device exists
   */
  public final boolean checkDeviceExists(final String deviceId) {
    UserData blah = userStore.get(User.UserData.class, deviceId);
    return blah != null;
  }

  /**
   * Gives the JSON representation of the messages.
   * @return the JSON representation of the messages.
   * @param deviceId the device to get messages for.
   */
  public final JSONArray getMessagesJson(final String deviceId) {
    JSONArray messagesJson = new JSONArray();
    User user = GetUserById(deviceId);
    if (user == null) {
      messagesJson.put(deviceId + " not in the db");
    }
    ArrayList<Message> messageList =
        user.messages;
    int messageNum = messageList.size();
    messagesJson.put(messageNum);
    for (int i = 0; i < messageNum; ++i) {
      JSONObject jsonObj = new JSONObject();
      jsonObj.put("ID", messageList.get(i).id);
      jsonObj.put("messageData", messageList.get(i).messageData.text);
      if (messageList.get(i).iterationsLeft == 0) {
        jsonObj.put("final", true);
      }
      jsonObj.put("latitude", messageList.get(i).startLocation.latitude);
      jsonObj.put("longitude", messageList.get(i).startLocation.longitude);
      jsonObj.put("accuracy", messageList.get(i).startLocation.accuracy);
      jsonObj.put("date", messageList.get(i).startLocation.date);
      jsonObj.put("name", GetUserById(messageList.get(i).rootSender).nickname);
      /*JSONArray annotationJson = new JSONArray();
      for (String annotation : messageList.get(i).annotationText) {
        annotationJson.put(annotation);
      }
      jsonObj.put("annotationData", annotationJson);
      /*JSONArray annotationJson = new JSONArray();
      for (Annotation annotation : messageList.get(i).annotations) {
        JSONObject annotationObj = new JSONObject();
        annotationObj.put("ID", annotation.id);
        annotationObj.put("name", annotation.poster);
        /*annotationObj.put("latitude", annotation.location.latitude);
        annotationObj.put("longitude", annotation.location.longitude);
        annotationObj.put("accuracy", annotation.location.accuracy);
        annotationObj.put("date", annotation.location.date);
        annotationObj.put("messageData", annotation.content.text);
        annotationJson.put(annotationObj);
      }
      jsonObj.put("annotations", annotationJson);*/
      messagesJson.put(jsonObj);
      //messagesJson.put(messageList.get(i).messageData.text);
    }
    return messagesJson;
  }

  /**
   * Adds a new device to the database.
   * @param deviceId the device id.
   * @param name the name to associate with it.
   * @return the success or failure.
   */
  public final boolean addNewDevice(final String deviceId,
      final String name) {
    User user = new User(deviceId, name, new Location(0, 0));
    AddUser(user);
    return true;
  }

  /**
   * Gets a JSONArray of all the users in the database. Shouldn't really
   * be here.
   * @return all of the users in JSONArray form.
   */
  public final JSONArray getUserList() {
    JSONArray userJson = new JSONArray();
    for (User user : GetAllUsers()) {
      JSONObject userObj = new JSONObject();
      userObj.put("ID", user.identity);
      userObj.put("name", user.nickname);
      userObj.put("numMessages", user.messages.size());
      userJson.put(userObj);
    }
    return userJson;
  }
  /**
   * Updates the users location in the db.
   * @param deviceId the device to update.
   * @param location the location to update to.
   * @return the success of the update.
   */
  public final boolean updateUserLocation(final String deviceId,
      final Location location) {
    User user = GetUserById(deviceId);
    if (user != null) {
      user.UpdateLocation(location);
      return true;
    } else {
      return false;
    }

  }
  /**
   * Creates and sets up a new message.
   * @param deviceId the original sender.
   * @param destinationId the eventual recepient.
   * @param mLocation the original location.
   * @param messageContent the content of the message.
   * @return the success of the method.
   */
  public final int sendNewMessage(final String deviceId,
      final Message message) {
    final int iterations = 2;

    User user = GetUserById(deviceId);

    user.UpdateLocation(message.startLocation);

    return user.AuthorNewMessage(message);
  }
  /**
   * Adds a new annotation to a message.
   * @param deviceId the device annotating.
   * @param messageId the id of the message.
   * @param messageData the data to add.
   * @return success or not.
   */
  public final boolean addNewAnnotation(final String deviceId,
      final String messageId, final String messageData) {
    User user = GetUserById(deviceId);
    for (Message m : user.messages) {
      if (m.id.equals(messageId)) {
        /*TextContent content = new TextContent();
        content.text = messageData;
        m.annotations.add(new Annotation(user, content));*/
        m.annotationText.add(messageData);
        User recipient = user.GetNextRecipient(m);
        if (recipient != null) {
          user.Send(m, recipient);
        }
        return true;
      }
    }
    return false;
  }
  /**
   * Calculates the latest message Id.
   * @return the latest message id.
   */
  public final int calcMessageId() {
    int currentMax = 0;
    for (User user : GetAllUsers()) {
      for (Message m : user.messages) {
        try {
          if (Integer.parseInt(m.id) > currentMax) {
            currentMax = Integer.parseInt(m.id);
          }
        } catch (NumberFormatException e) {
          System.out.println("test");
        }
      }
    }
    return currentMax;
  }
}
